/**
 * A date picker. This class is used by the Ext.form.field.Date field to allow
 * browsing and selection of valid dates in a popup next to the field, but may
 * also be used with other components.
 * 
 * Typically you will need to implement a handler function to be notified when
 * the user chooses a date from the picker; you can register the handme.hourBtn
 * ler using the {@link #select} event, or by implementing the {@link #handler}
 * method.
 * 
 * By default the user will be allowed to pick any date; this can be changed by
 * using the {@link #minDate}, {@link #maxDate}, {@link #disabledDays},
 * {@link #disabledDatesRE}, and/or {@link #disabledDates} configs.
 * 
 * All the string values documented below may be overridden by including an Ext
 * locale file in your page.
 * 
 * @example Ext.create('Ext.panel.Panel', { title: 'Choose a future date:',
 *          width: 200, bodyPadding: 10, renderTo: Ext.getBody(), items: [{
 *          xtype: 'datepicker', minDate: new Date(), handler: function(picker,
 *          date) { // do something with the selected date } }] });
 *         
 *          由jfok 于 2016年07月14日制作完成。
 *          
 *          jfok1972@qq.com
 *          
 */
Ext
		.define(
				'app.ux.picker.DateTime',
				{
					extend : 'Ext.Component',
					requires : [ 'Ext.XTemplate', 'Ext.button.Button',
							'Ext.button.Split', 'Ext.util.ClickRepeater', 'Ext.util.KeyNav',
							'Ext.fx.Manager', 'Ext.picker.Month' ],
					alias : 'widget.datetimepicker',
					alternateClassName : 'Ext.DateTimePicker',
					okText : '确定',
					// <locale>
					/**
					 * @cfg {String} todayText The text to display on the button that
					 *      selects the current date
					 */
					todayText : 'Today',
					// </locale>

					// <locale>
					/**
					 * @cfg {String} ariaTitle The text to display for the aria title
					 */
					ariaTitle : 'Date Picker: {0}',
					// </locale>

					// <locale>
					/**
					 * @cfg {String} ariaTitleDateFormat The date format to display for
					 *      the current value in the {@link #ariaTitle}
					 */
					ariaTitleDateFormat : 'F d',
					// </locale>

					/**
					 * @cfg {Function} handler Optional. A function that will handle the
					 *      select event of this picker. The handler is passed the
					 *      following parameters: - `picker` : Ext.picker.Date
					 * 
					 * This Date picker. - `date` : Date
					 * 
					 * The selected date.
					 */

					/**
					 * @cfg {Object} scope The scope (`this` reference) in which the `{@link #handler}`
					 *      function will be called.
					 * 
					 * Defaults to this DatePicker instance.
					 */

					// <locale>
					/**
					 * @cfg {String} todayTip A string used to format the message for
					 *      displaying in a tooltip over the button that selects the
					 *      current date. The `{0}` token in string is replaced by today's
					 *      date.
					 */
					todayTip : '{0} (Spacebar)',
					// </locale>

					// <locale>
					/**
					 * @cfg {String} minText The error text to display if the minDate
					 *      validation fails.
					 */
					minText : 'This date is before the minimum date',
					// </locale>

					// <locale>
					/**
					 * @cfg {String} ariaMinText The text that will be announced by
					 *      Assistive Technologies such as screen readers when user is
					 *      navigating to the cell which date is less than
					 *      {@link #minDate}.
					 */
					ariaMinText : "This date is before the minimum date",
					// </locale>

					// <locale>
					/**
					 * @cfg {String} maxText The error text to display if the maxDate
					 *      validation fails.
					 */
					maxText : 'This date is after the maximum date',
					// </locale>

					// <locale>
					/**
					 * @cfg {String} ariaMaxText The text that will be announced by
					 *      Assistive Technologies such as screen readers when user is
					 *      navigating to the cell which date is later than
					 *      {@link #maxDate}.
					 */
					ariaMaxText : "This date is after the maximum date",
					// </locale>

					/**
					 * @cfg {String} format The default date format string which can be
					 *      overriden for localization support. The format must be valid
					 *      according to {@link Ext.Date#parse} (defaults to
					 *      {@link Ext.Date#defaultFormat}).
					 */

					// <locale>
					/**
					 * @cfg {String} disabledDaysText The tooltip to display when the date
					 *      falls on a disabled day.
					 */
					disabledDaysText : 'Disabled',
					// </locale>

					// <locale>
					/**
					 * @cfg {String} ariaDisabledDaysText The text that Assistive
					 *      Technologies such as screen readers will announce when the
					 *      date falls on a disabled day of week.
					 */
					ariaDisabledDaysText : "This day of week is disabled",
					// </locale>

					// <locale>
					/**
					 * @cfg {String} disabledDatesText The tooltip text to display when
					 *      the date falls on a disabled date.
					 */
					disabledDatesText : 'Disabled',
					// </locale>

					// <locale>
					/**
					 * @cfg {String} ariaDisabledDatesText The text that Assistive
					 *      Technologies such as screen readers will announce when the
					 *      date falls on a disabled date.
					 */
					ariaDisabledDatesText : "This date is disabled",

					// </locale>
					/**
					 * @cfg {String[]} monthNames An array of textual month names which
					 *      can be overriden for localization support (defaults to
					 *      Ext.Date.monthNames)
					 * @deprecated This config is deprecated. In future the month names
					 *             will be retrieved from {@link Ext.Date}
					 */

					/**
					 * @cfg {String[]} dayNames An array of textual day names which can be
					 *      overriden for localization support (defaults to
					 *      Ext.Date.dayNames)
					 * @deprecated This config is deprecated. In future the day names will
					 *             be retrieved from {@link Ext.Date}
					 */

					// <locale>
					/**
					 * @cfg {String} nextText The next month navigation button tooltip
					 */
					nextText : 'Next Month (Control+Right)',
					// </locale>

					// <locale>
					/**
					 * @cfg {String} prevText The previous month navigation button tooltip
					 */
					prevText : 'Previous Month (Control+Left)',
					// </locale>

					// <locale>
					/**
					 * @cfg {String} monthYearText The header month selector tooltip
					 */
					monthYearText : 'Choose a month (Control+Up/Down to move years)',
					// </locale>

					// <locale>
					/**
					 * @cfg {String} monthYearFormat The date format for the header month
					 */
					monthYearFormat : 'F Y',
					// </locale>

					// <locale>
					/**
					 * @cfg {Number} [startDay=undefined] Day index at which the week
					 *      should begin, 0-based.
					 * 
					 * Defaults to `0` (Sunday).
					 */
					startDay : 0,
					// </locale>

					// <locale>
					/**
					 * @cfg {Boolean} showToday False to hide the footer area containing
					 *      the Today button and disable the keyboard handler for spacebar
					 *      that selects the current date.
					 */
					showToday : true,
					// </locale>

					/**
					 * @cfg {Date} [minDate=null] Minimum allowable date (JavaScript date
					 *      object)
					 */

					/**
					 * @cfg {Date} [maxDate=null] Maximum allowable date (JavaScript date
					 *      object)
					 */

					/**
					 * @cfg {Number[]} [disabledDays=null] An array of days to disable,
					 *      0-based. For example, [0, 6] disables Sunday and Saturday.
					 */

					/**
					 * @cfg {RegExp} [disabledDatesRE=null] JavaScript regular expression
					 *      used to disable a pattern of dates. The {@link #disabledDates}
					 *      config will generate this regex internally, but if you specify
					 *      disabledDatesRE it will take precedence over the disabledDates
					 *      value.
					 */

					/**
					 * @cfg {String[]} disabledDates An array of 'dates' to disable, as
					 *      strings. These strings will be used to build a dynamic regular
					 *      expression so they are very powerful. Some examples: -
					 *      ['03/08/2003', '09/16/2003'] would disable those exact dates -
					 *      ['03/08', '09/16'] would disable those days for every year -
					 *      ['^03/08'] would only match the beginning (useful if you are
					 *      using short years) - ['03/../2006'] would disable every day in
					 *      March 2006 - ['^03'] would disable every day in every March
					 * 
					 * Note that the format of the dates included in the array should
					 * exactly match the {@link #format} config. In order to support
					 * regular expressions, if you are using a date format that has '.' in
					 * it, you will have to escape the dot when restricting dates. For
					 * example: ['03\\.08\\.03'].
					 */

					/**
					 * @cfg {Boolean} disableAnim True to disable animations when showing
					 *      the month picker.
					 */
					disableAnim : false,

					/**
					 * @cfg {String} [baseCls='x-datepicker'] The base CSS class to apply
					 *      to this components element.
					 */
					baseCls : Ext.baseCSSPrefix + 'datepicker',

					/**
					 * @cfg {String} [selectedCls='x-datepicker-selected'] The class to
					 *      apply to the selected cell.
					 */

					/**
					 * @cfg {String} [disabledCellCls='x-datepicker-disabled'] The class
					 *      to apply to disabled cells.
					 */

					// <locale>
					/**
					 * @cfg {String} longDayFormat The format for displaying a date in a
					 *      longer format.
					 */
					longDayFormat : 'F d, Y',
					// </locale>

					/**
					 * @cfg {Object} keyNavConfig Specifies optional custom key event
					 *      handlers for the {@link Ext.util.KeyNav} attached to this date
					 *      picker. Must conform to the config format recognized by the
					 *      {@link Ext.util.KeyNav} constructor. Handlers specified in
					 *      this object will replace default handlers of the same name.
					 */

					/**
					 * @cfg {String} The {@link Ext.button.Button#ui} to use for the date
					 *      picker's footer buttons.
					 */
					footerButtonUI : 'default',

					isDatePicker : true,
					alignOnScroll : false,

					ariaRole : 'region',
					focusable : true,

					childEls : [ 'innerEl', 'eventEl', 'prevEl', 'nextEl', 'middleBtnEl',
							'footerEl' ],

					border : true,

					/**
					 * @cfg
					 * @inheritdoc
					 */
					renderTpl : [
							'<div id="{id}-innerEl" data-ref="innerEl" role="presentation">',
							'<div class="{baseCls}-header">',
							'<div id="{id}-prevEl" data-ref="prevEl" class="{baseCls}-prev {baseCls}-arrow" role="presentation" title="{prevText}"></div>',
							'<div id="{id}-middleBtnEl" data-ref="middleBtnEl" class="{baseCls}-month" role="heading">{%this.renderMonthBtn(values, out)%}</div>',
							'<div id="{id}-nextEl" data-ref="nextEl" class="{baseCls}-next {baseCls}-arrow" role="presentation" title="{nextText}"></div>',
							'</div>',
							'<table role="grid" id="{id}-eventEl" data-ref="eventEl" class="{baseCls}-inner" cellspacing="0" tabindex="0" aria-readonly="true">',
							'<thead>',
							'<tr role="row">',
							'<tpl for="dayNames">',
							'<th role="columnheader" class="{parent.baseCls}-column-header" aria-label="{.}">',
							'<div role="presentation" class="{parent.baseCls}-column-header-inner">{.:this.firstInitial}</div>',
							'</th>',
							'</tpl>',
							'</tr>',
							'</thead>',
							'<tbody>',
							'<tr role="row">',
							'<tpl for="days">',
							'{#:this.isEndOfWeek}',
							'<td role="gridcell">',
							'<div hidefocus="on" class="{parent.baseCls}-date"></div>',
							'</td>',
							'</tpl>',
							'</tr>',
							'</tbody>',
							'</table>',

							// 指定时分秒渲染框架
							'<table id="{id}-timeEl" style="table-layout:auto;margin:0 auto;width:90%;text-align:center;" class="x-datepicker-inner" cellspacing="0">',
							'<tbody><tr>',
							'<td width="30%">{%this.renderHourField(values,out)%}</td>',
							'<td width="35%">{%this.renderMinuteField(values,out)%}</td>',
							'<td width="35%">{%this.renderSecondField(values,out)%}</td>',
							'</tr></tbody>',
							'</table>',
							// 上面是新增的

							'<tpl if="showToday">',
							'<div id="{id}-footerEl" data-ref="footerEl" role="presentation" class="{baseCls}-footer">',
							// 增加了确认按钮
							'{%this.renderOkBtn(values, out)%}',
							// 上面是新增的
							'{%this.renderTodayBtn(values, out)%}',
							'</div>',
							'</tpl>',
							// These elements are used with Assistive Technologies such as
							// screen readers
							'<div id="{id}-todayText" class="' + Ext.baseCSSPrefix
									+ 'hidden-clip">{todayText}.</div>',
							'<div id="{id}-ariaMinText" class="' + Ext.baseCSSPrefix
									+ 'hidden-clip">{ariaMinText}.</div>',
							'<div id="{id}-ariaMaxText" class="' + Ext.baseCSSPrefix
									+ 'hidden-clip">{ariaMaxText}.</div>',
							'<div id="{id}-ariaDisabledDaysText" class="' + Ext.baseCSSPrefix
									+ 'hidden-clip">{ariaDisabledDaysText}.</div>',
							'<div id="{id}-ariaDisabledDatesText" class="'
									+ Ext.baseCSSPrefix
									+ 'hidden-clip">{ariaDisabledDatesText}.</div>',
							'</div>',
							{
								firstInitial : function(value) {
									return Ext.picker.Date.prototype.getDayInitial(value);
								},
								isEndOfWeek : function(value) {
									// convert from 1 based index to 0 based
									// by decrementing value once.
									value--;
									var end = value % 7 === 0 && value !== 0;
									return end ? '</tr><tr role="row">' : '';
								},
								renderTodayBtn : function(values, out) {
									Ext.DomHelper.generateMarkup(values.$comp.todayBtn
											.getRenderTree(), out);
								},
								renderMonthBtn : function(values, out) {
									Ext.DomHelper.generateMarkup(values.$comp.monthBtn
											.getRenderTree(), out);
								},
								// 指定渲染方法调用
								renderHourField : function(values, out) {
									Ext.DomHelper.generateMarkup(values.$comp.hourField
											.getRenderTree(), out);
								},
								renderMinuteField : function(values, out) {
									Ext.DomHelper.generateMarkup(values.$comp.minuteField
											.getRenderTree(), out);
								},
								renderSecondField : function(values, out) {
									Ext.DomHelper.generateMarkup(values.$comp.secondField
											.getRenderTree(), out);
								},
								renderOkBtn : function(values, out) {
									Ext.DomHelper.generateMarkup(values.$comp.okBtn
											.getRenderTree(), out);
								}
							// 以上四个函数是新增的
							} ],

					// Default value used to initialise each date in the DatePicker.
					// __Note:__ 12 noon was chosen because it steers well clear of all
					// DST timezone changes.
					initHour : 12, // 24-hour format

					numDays : 42,

					/**
					 * @event select Fires when a date is selected
					 * @param {Ext.picker.Date}
					 *          this DatePicker
					 * @param {Date}
					 *          date The selected date
					 */

					initComponent : function() {
						var me = this;

						me.selectedCls = me.baseCls + '-selected';
						me.disabledCellCls = me.baseCls + '-disabled';
						me.prevCls = me.baseCls + '-prevday';
						me.activeCls = me.baseCls + '-active';
						me.cellCls = me.baseCls + '-cell';
						me.nextCls = me.baseCls + '-prevday';
						me.todayCls = me.baseCls + '-today';

						if (!me.format) {
							me.format = Ext.Date.defaultFormat;
						}
						if (!me.dayNames) {
							me.dayNames = Ext.Date.dayNames;
						}
						me.dayNames = me.dayNames.slice(me.startDay).concat(
								me.dayNames.slice(0, me.startDay));

						me.callParent();

						me.value = me.value ? (me.value) : (new Date());

						me.initDisabledDays();
					},

					// Keep the tree structure correct for Ext.form.field.Picker input
					// fields which poke a 'pickerField' reference down into their pop-up
					// pickers.
					getRefOwner : function() {
						return this.pickerField || this.callParent();
					},

					getRefItems : function() {
						var results = [], monthBtn = this.monthBtn, todayBtn = this.todayBtn;

						if (monthBtn) {
							results.push(monthBtn);
						}

						if (todayBtn) {
							results.push(todayBtn);
						}
						return results;
					},

					beforeRender : function() {
						/*
						 * days array for looping through 6 full weeks (6 weeks * 7 days)
						 * Note that we explicitly force the size here so the template
						 * creates all the appropriate cells.
						 */
						var me = this, encode = Ext.String.htmlEncode, days = new Array(
								me.numDays), today = Ext.Date.format(new Date(), me.format);

						if (me.padding && !me.width) {
							me.cacheWidth();
						}

						// 自己加的
						me.hourField = new Ext.form.field.Number({
							ownerCt : me,
							ownerLayout : me.getComponentLayout(),
							minValue : 0,
							maxValue : 23,
							step : 1,
							width : '100%',
							enableKeyEvents : true,
							listeners : {
								specialkey : function(field, e) {
									if (e.getKey() == e.ENTER) {
										e.stopEvent();
										me.minuteField.focus(true);
									}
								}
							}
						});
						me.minuteField = new Ext.form.field.Number({
							ownerCt : me,
							ownerLayout : me.getComponentLayout(),
							minValue : 0,
							maxValue : 59,
							step : 1,
							width : '100%',
							labelWidth : 10,
							fieldLabel : '&nbsp;',
							enableKeyEvents : true,
							listeners : {
								specialkey : function(field, e) {
									if (e.getKey() == e.ENTER) {
										e.stopEvent();
										me.secondField.focus(true);
									}
								}
							}
						});
						me.secondField = new Ext.form.field.Number({
							ownerCt : me,
							ownerLayout : me.getComponentLayout(),
							minValue : 0,
							maxValue : 59,
							step : 1,
							width : '100%',
							labelWidth : 10,
							fieldLabel : '&nbsp;',
							enableKeyEvents : true,
							listeners : {
								specialkey : function(field, e) {
									if (e.getKey() == e.ENTER) {
										e.stopEvent();
										me.okBtn.focus(true);
									}
								}
							}
						});
						// 自己加的

						me.monthBtn = new Ext.button.Split({
							ownerCt : me,
							ownerLayout : me.getComponentLayout(),
							text : '',
							tooltip : me.monthYearText,
							tabIndex : -1,
							ariaRole : 'presentation',
							listeners : {
								click : me.doShowMonthPicker,
								arrowclick : me.doShowMonthPicker,
								scope : me
							}
						});

						if (me.showToday) {

							// 自己加的
							me.okBtn = new Ext.button.Button({
								ui : me.footerButtonUI,
								ownerCt : me,
								ownerLayout : me.getComponentLayout(),
								text : me.okText,
								handler : me.okHandler, // 确认按钮的事件委托
								scope : me
							});
							// 自己加的

							me.todayBtn = new Ext.button.Button({
								ui : me.footerButtonUI,
								ownerCt : me,
								ownerLayout : me.getComponentLayout(),
								text : Ext.String.format(me.todayText, today),
								tooltip : Ext.String.format(me.todayTip, today),
								tooltipType : 'title',
								tabIndex : -1,
								ariaRole : 'presentation',
								handler : me.selectToday,
								scope : me
							});
						}

						me.callParent();

						Ext.applyIf(me, {
							renderData : {}
						});

						Ext.apply(me.renderData, {
							dayNames : me.dayNames,
							showToday : me.showToday,
							prevText : encode(me.prevText),
							nextText : encode(me.nextText),
							todayText : encode(me.todayText),
							ariaMinText : encode(me.ariaMinText),
							ariaMaxText : encode(me.ariaMaxText),
							ariaDisabledDaysText : encode(me.ariaDisabledDaysText),
							ariaDisabledDatesText : encode(me.ariaDisabledDatesText),
							days : days
						});

						me.protoEl.unselectable();
					},

					/**
					 * 确认 按钮触发的调用
					 */
					okHandler : function() {
						var me = this, btn = me.okBtn;
						if (btn && !btn.disabled) {
							me.setValue(this.getValue());
							me.fireEvent('select', me, me.value);
							me.onSelect();
						}
					},

					cacheWidth : function() {
						var me = this, padding = me.parseBox(me.padding), widthEl = Ext
								.getBody().createChild({
									cls : me.baseCls + ' ' + me.borderBoxCls,
									style : 'position:absolute;top:-1000px;left:-1000px;'
								});

						me.self.prototype.width = widthEl.getWidth() + padding.left
								+ padding.right;
						widthEl.destroy();
					},

					/**
					 * @inheritdoc
					 * @private
					 */
					onRender : function(container, position) {
						var me = this;

						me.callParent(arguments);

						me.cells = me.eventEl.select('tbody td');
						me.textNodes = me.eventEl.query('tbody td div');

						me.eventEl.set({
							'aria-labelledby' : me.monthBtn.id
						});

						me.mon(me.eventEl, {
							scope : me,
							mousewheel : me.handleMouseWheel,
							click : {
								fn : me.handleDateClick,
								delegate : 'div.' + me.baseCls + '-date'
							}
						});

					},

					/**
					 * @inheritdoc
					 * @private
					 */
					initEvents : function() {
						var me = this;

						me.callParent();

						// If we're part of a date field, don't allow us to focus on
						// mousedown,
						// the field will handle that. If we are standalone, then allow the
						// default
						// behaviour to occur to receive focus
						if (me.pickerField) {
							me.el.on('mousedown', me.onMouseDown, me);
						}

						// Month button is pointer interactive only, it should not be
						// allowed to focus.
						me.monthBtn.el.on('mousedown', me.onMouseDown, me);

						me.prevRepeater = new Ext.util.ClickRepeater(me.prevEl, {
							handler : me.showPrevMonth,
							scope : me,
							mousedownStopEvent : true
						});

						me.nextRepeater = new Ext.util.ClickRepeater(me.nextEl, {
							handler : me.showNextMonth,
							scope : me,
							mousedownStopEvent : true
						});

						me.keyNav = new Ext.util.KeyNav(
								me.eventEl,
								Ext
										.apply(
												{
													scope : me,

													left : function(e) {
														if (e.ctrlKey) {
															this.showPrevMonth();
														} else {
															this.update(Ext.Date.add(this.activeDate,
																	Ext.Date.DAY, -1));
														}

														// We need to prevent default to avoid scrolling the
														// nearest container
														// which in case of a floating Date picker will be
														// the document body.
														// This applies to all navigation keys.
														e.preventDefault();
													},

													right : function(e) {
														if (e.ctrlKey) {
															this.showNextMonth();
														} else {
															this.update(Ext.Date.add(this.activeDate,
																	Ext.Date.DAY, 1));
														}

														e.preventDefault();
													},

													up : function(e) {
														// This is non-standard behavior kept for backward
														// compatibility.
														// Ctrl-PageUp is reverse to this and it should be
														// used instead.
														if (e.ctrlKey) {
															this.showNextYear();
														} else {
															this.update(Ext.Date.add(this.activeDate,
																	Ext.Date.DAY, -7));
														}

														e.preventDefault();
													},

													down : function(e) {
														// This is non-standard behavior kept for backward
														// compatibility.
														// Ctrl-PageDown is reverse to this and it should be
														// used instead.
														if (e.ctrlKey) {
															this.showPrevYear();
														} else {
															this.update(Ext.Date.add(this.activeDate,
																	Ext.Date.DAY, 7));
														}

														e.preventDefault();
													},

													pageUp : function(e) {
														if (e.ctrlKey) {
															this.showPrevYear();
														} else {
															this.showPrevMonth();
														}

														e.preventDefault();
													},

													pageDown : function(e) {
														if (e.ctrlKey) {
															this.showNextYear();
														} else {
															this.showNextMonth();
														}

														e.preventDefault();
													},

													home : function(e) {
														this.update(Ext.Date
																.getFirstDateOfMonth(this.activeDate));

														e.preventDefault();
													},

													end : function(e) {
														this.update(Ext.Date
																.getLastDateOfMonth(this.activeDate));

														e.preventDefault();
													},

													tab : function(e) {
														// When the picker is floating and attached to an
														// input field, its
														// 'select' handler will focus the inputEl so when
														// navigation happens
														// it does so as if the input field was focused all
														// the time.
														// This is the desired behavior and we try not to
														// interfere with it
														// in the picker itself, see below.
														this.handleTabKey(e);

														// Allow default behaviour of TAB - it MUST be
														// allowed to navigate.
														return true;
													},

													enter : function(e) {
														this.handleDateClick(e, this.activeCell.firstChild);
													},

													space : function(e) {
														var me = this, pickerField = me.pickerField, startValue, value, pickerValue;

														me.setValue(new Date(
																me.activeCell.firstChild.dateValue));

														if (pickerField) {
															startValue = me.startValue;
															value = me.value;
															pickerValue = pickerField.getValue();

															if (pickerValue && startValue
																	&& pickerValue.getTime() === value.getTime()) {
																pickerField.setValue(startValue);
															} else {
																pickerField.setValue(value);
															}
														}

														// Space key causes scrolling, too :(
														e.preventDefault();
													}
												}, me.keyNavConfig));

						if (me.disabled) {
							me.syncDisabled(true, true);
						}

						me.update(me.value);
					},

					onMouseDown : function(e) {
						if (e.target.id.indexOf("numberfield") !== 0)
							e.preventDefault();
					},

					handleTabKey : function(e) {
						var me = this, t = me.getSelectedDate(me.activeDate), handler = me.handler;

						// The following code is like handleDateClick without the
						// e.stopEvent()
						if (!me.disabled && t.dateValue
								&& !Ext.fly(t.parentNode).hasCls(me.disabledCellCls)) {
							me.setValue(new Date(t.dateValue));
							me.fireEvent('select', me, me.value);
							if (handler) {
								handler.call(me.scope || me, me, me.value);
							}
							me.onSelect();
						}
						// Even if the above condition is not met we have to let the field
						// know
						// that we're tabbing out - that's user action we can do nothing
						// about
						else {
							me.fireEventArgs('tabout', [ me ]);
						}
					},

					getSelectedDate : function(date) {
						var me = this, t = date.getTime(), cells = me.cells, cls = me.selectedCls, cellItems = cells.elements, cLen = cellItems.length, cell, c;

						cells.removeCls(cls);

						for (c = 0; c < cLen; c++) {
							cell = cellItems[c].firstChild;
							if (cell.dateValue === t) {
								return cell;
							}
						}
						return null;
					},

					/**
					 * Setup the disabled dates regex based on config options
					 * 
					 * @private
					 */
					initDisabledDays : function() {
						var me = this, dd = me.disabledDates, re = '(?:', len, d, dLen, dI;

						if (!me.disabledDatesRE && dd) {
							len = dd.length - 1;

							dLen = dd.length;

							for (d = 0; d < dLen; d++) {
								dI = dd[d];

								re += Ext.isDate(dI) ? '^'
										+ Ext.String
												.escapeRegex(Ext.Date.dateFormat(dI, me.format)) + '$'
										: dI;
								if (d !== len) {
									re += '|';
								}
							}

							me.disabledDatesRE = new RegExp(re + ')');
						}
					},

					/**
					 * Replaces any existing disabled dates with new values and refreshes
					 * the DatePicker.
					 * 
					 * @param {String[]/RegExp}
					 *          disabledDates An array of date strings (see the
					 *          {@link #disabledDates} config for details on supported
					 *          values), or a JavaScript regular expression used to
					 *          disable a pattern of dates.
					 * @return {Ext.picker.Date} this
					 */
					setDisabledDates : function(dd) {
						var me = this;

						if (Ext.isArray(dd)) {
							me.disabledDates = dd;
							me.disabledDatesRE = null;
						} else {
							me.disabledDatesRE = dd;
						}
						me.initDisabledDays();
						me.update(me.value, true);
						return me;
					},

					/**
					 * Replaces any existing disabled days (by index, 0-6) with new values
					 * and refreshes the DatePicker.
					 * 
					 * @param {Number[]}
					 *          disabledDays An array of disabled day indexes. See the
					 *          {@link #disabledDays} config for details on supported
					 *          values.
					 * @return {Ext.picker.Date} this
					 */
					setDisabledDays : function(dd) {
						this.disabledDays = dd;
						return this.update(this.value, true);
					},

					/**
					 * Replaces any existing {@link #minDate} with the new value and
					 * refreshes the DatePicker.
					 * 
					 * @param {Date}
					 *          value The minimum date that can be selected
					 * @return {Ext.picker.Date} this
					 */
					setMinDate : function(dt) {
						this.minDate = dt;
						return this.update(this.value, true);
					},

					/**
					 * Replaces any existing {@link #maxDate} with the new value and
					 * refreshes the DatePicker.
					 * 
					 * @param {Date}
					 *          value The maximum date that can be selected
					 * @return {Ext.picker.Date} this
					 */
					setMaxDate : function(dt) {
						this.maxDate = dt;
						return this.update(this.value, true);
					},

					/**
					 * Sets the value of the date field
					 * 
					 * @param {Date}
					 *          value The date to set
					 * @return {Ext.picker.Date} this
					 */
					setValue : function(value, isfixed) {
						// If passed a null value just pass in a new date object.

						var me = this;
						// 这里一定要用 Ext.Date.clone ，不然不会触发 isDirty事件
						this.value = Ext.Date.clone(value || new Date());

						if (isfixed !== true) {
							this.value.setHours(me.hourField.getValue());
							this.value.setMinutes(me.minuteField.getValue());
							this.value.setSeconds(me.secondField.getValue());
						}

						return this.update(this.value);
					},

					/**
					 * Gets the current selected value of the date field
					 * 
					 * @return {Date} The selected date
					 */
					getValue : function() {
						return this.value;
					},

					// <locale type="function">
					/**
					 * Gets a single character to represent the day of the week
					 * 
					 * @return {String} The character
					 */
					getDayInitial : function(value) {
						return value.substr(0, 1);
					},
					// </locale>

					/**
					 * @inheritdoc
					 * @private
					 */
					onEnable : function() {
						var me = this;

						me.callParent();
						me.syncDisabled(false, true);
						me.update(me.activeDate);

					},

					/**
					 * @inheritdoc
					 * @private
					 */
					onShow : function() {
						var me = this;

						me.callParent();
						me.syncDisabled(false);
						if (me.pickerField) {
							me.startValue = me.pickerField.getValue();
						}
					},

					/**
					 * @inheritdoc
					 * @private
					 */
					onHide : function() {
						this.callParent();
						this.syncDisabled(true);
					},

					/**
					 * @inheritdoc
					 * @private
					 */
					onDisable : function() {
						this.callParent();
						this.syncDisabled(true, true);
					},

					/**
					 * Get the current active date.
					 * 
					 * @private
					 * @return {Date} The active date
					 */
					getActive : function() {
						return this.activeDate || this.value;
					},

					/**
					 * Run any animation required to hide/show the month picker.
					 * 
					 * @private
					 * @param {Boolean}
					 *          isHide True if it's a hide operation
					 */
					runAnimation : function(isHide) {
						var picker = this.monthPicker, options = {
							duration : 200,
							callback : function() {
								picker.setVisible(!isHide);
							}
						};

						if (isHide) {
							picker.el.slideOut('t', options);
						} else {
							picker.el.slideIn('t', options);
						}
					},

					/**
					 * Hides the month picker, if it's visible.
					 * 
					 * @param {Boolean}
					 *          [animate] Indicates whether to animate this action. If the
					 *          animate parameter is not specified, the behavior will use
					 *          {@link #disableAnim} to determine whether to animate or
					 *          not.
					 * @return {Ext.picker.Date} this
					 */
					hideMonthPicker : function(animate) {
						var me = this, picker = me.monthPicker;

						if (picker && picker.isVisible()) {
							if (me.shouldAnimate(animate)) {
								me.runAnimation(true);
							} else {
								picker.hide();
							}
						}
						return me;
					},

					doShowMonthPicker : function() {
						// Wrap in an extra call so we can prevent the button
						// being passed as an animation parameter.
						this.showMonthPicker();
					},

					doHideMonthPicker : function() {
						// Wrap in an extra call so we can prevent this
						// being passed as an animation parameter
						this.hideMonthPicker();
					},

					/**
					 * Show the month picker
					 * 
					 * @param {Boolean}
					 *          [animate] Indicates whether to animate this action. If the
					 *          animate parameter is not specified, the behavior will use
					 *          {@link #disableAnim} to determine whether to animate or
					 *          not.
					 * @return {Ext.picker.Date} this
					 */
					showMonthPicker : function(animate) {
						var me = this, el = me.el, picker;

						if (me.rendered && !me.disabled) {
							picker = me.createMonthPicker();
							if (!picker.isVisible()) {
								picker.setValue(me.getActive());
								picker.setSize(el.getSize());

								// Null out floatParent so that the [-1, -1] position is not
								// made relative to this
								picker.floatParent = null;
								picker.setPosition(-el.getBorderWidth('l'), -el
										.getBorderWidth('t'));
								if (me.shouldAnimate(animate)) {
									me.runAnimation(false);
								} else {
									picker.show();
								}
							}
						}
						return me;
					},

					/**
					 * Checks whether a hide/show action should animate
					 * 
					 * @private
					 * @param {Boolean}
					 *          [animate] A possible animation value
					 * @return {Boolean} Whether to animate the action
					 */
					shouldAnimate : function(animate) {
						return Ext.isDefined(animate) ? animate : !this.disableAnim;
					},

					/**
					 * Create the month picker instance
					 * 
					 * @private
					 * @return {Ext.picker.Month} picker
					 */
					createMonthPicker : function() {
						var me = this, picker = me.monthPicker;

						if (!picker) {
							me.monthPicker = picker = new Ext.picker.Month({
								renderTo : me.el,
								// We need to set the ownerCmp so that owns() can correctly
								// match up the component hierarchy so that focus does not leave
								// an owning picker field if/when this gets focus.
								ownerCmp : me,
								floating : true,
								padding : me.padding,
								shadow : false,
								small : me.showToday === false,
								footerButtonUI : me.footerButtonUI,
								listeners : {
									scope : me,
									cancelclick : me.onCancelClick,
									okclick : me.onOkClick,
									yeardblclick : me.onOkClick,
									monthdblclick : me.onOkClick
								}
							});
							if (!me.disableAnim) {
								// hide the element if we're animating to prevent an initial
								// flicker
								picker.el.setStyle('display', 'none');
							}
							picker.hide();
							me.on('beforehide', me.doHideMonthPicker, me);
						}
						return picker;
					},

					/**
					 * Respond to an ok click on the month picker
					 * 
					 * @private
					 */
					onOkClick : function(picker, value) {
						var me = this, month = value[0], year = value[1], date = new Date(
								year, month, me.getActive().getDate());

						if (date.getMonth() !== month) {
							// 'fix' the JS rolling date conversion if needed
							date = Ext.Date.getLastDateOfMonth(new Date(year, month, 1));
						}
						me.setValue(date);
						me.hideMonthPicker();
					},

					/**
					 * Respond to a cancel click on the month picker
					 * 
					 * @private
					 */
					onCancelClick : function() {
						this.selectedUpdate(this.activeDate);
						this.hideMonthPicker();
					},

					/**
					 * Show the previous month.
					 * 
					 * @param {Object}
					 *          e
					 * @return {Ext.picker.Date} this
					 */
					showPrevMonth : function(e) {
						return this.setValue(Ext.Date.add(this.activeDate, Ext.Date.MONTH,
								-1));
					},

					/**
					 * Show the next month.
					 * 
					 * @param {Object}
					 *          e
					 * @return {Ext.picker.Date} this
					 */
					showNextMonth : function(e) {
						return this.setValue(Ext.Date.add(this.activeDate, Ext.Date.MONTH,
								1));
					},

					/**
					 * Show the previous year.
					 * 
					 * @return {Ext.picker.Date} this
					 */
					showPrevYear : function() {
						return this.setValue(Ext.Date.add(this.activeDate, Ext.Date.YEAR,
								-1));
					},

					/**
					 * Show the next year.
					 * 
					 * @return {Ext.picker.Date} this
					 */
					showNextYear : function() {
						return this.setValue(Ext.Date
								.add(this.activeDate, Ext.Date.YEAR, 1));
					},

					/**
					 * Respond to the mouse wheel event
					 * 
					 * @private
					 * @param {Ext.event.Event}
					 *          e
					 */
					handleMouseWheel : function(e) {
						var delta;

						e.stopEvent();

						if (!this.disabled) {
							delta = e.getWheelDelta();

							if (delta > 0) {
								this.showPrevMonth();
							} else if (delta < 0) {
								this.showNextMonth();
							}
						}
					},

					/**
					 * Respond to a date being clicked in the picker
					 * 
					 * @private
					 * @param {Ext.event.Event}
					 *          e
					 * @param {HTMLElement}
					 *          t
					 */
					handleDateClick : function(e, t) {
						var me = this, handler = me.handler;

						e.stopEvent();

						if (!me.disabled && t.dateValue
								&& !Ext.fly(t.parentNode).hasCls(me.disabledCellCls)) {
							me.setValue(new Date(t.dateValue));
							me.fireEvent('select', me, me.value);

							if (handler) {
								handler.call(me.scope || me, me, me.value);
							}

							// event handling is turned off on hide
							// when we are using the picker in a field
							// therefore onSelect comes AFTER the select
							// event.
							me.onSelect();
						}
					},

					/**
					 * Perform any post-select actions
					 * 
					 * @private
					 */
					onSelect : function() {
						if (this.hideOnSelect) {
							this.hide();
						}
					},

					/**
					 * Sets the current value to today.
					 * 
					 * @return {Ext.picker.Date} this
					 */
					selectToday : function() {
						var me = this, btn = me.todayBtn, handler = me.handler;

						if (btn && !btn.disabled) {
							var date = new Date();
							me.hourField.setValue(date.getHours());
							me.minuteField.setValue(date.getMinutes());
							me.secondField.setValue(date.getSeconds());
							me.setValue(date);
							me.fireEvent('select', me, me.value);
							if (handler) {
								handler.call(me.scope || me, me, me.value);
							}
							me.onSelect();
						}
						return me;
					},

					/**
					 * Update the selected cell
					 * 
					 * @private
					 * @param {Date}
					 *          date The new date
					 */
					selectedUpdate : function(date) {
						var me = this, t = Ext.Date.clearTime(date, true).getTime(), cells = me.cells, cls = me.selectedCls, c, cLen = cells
								.getCount(), cell;

						me.eventEl.dom.setAttribute('aria-busy', 'true');

						cell = me.activeCell;

						if (cell) {
							Ext.fly(cell).removeCls(cls);
							cell.setAttribute('aria-selected', false);
						}

						for (c = 0; c < cLen; c++) {
							cell = cells.item(c);

							if (me.textNodes[c].dateValue === t) {
								me.activeCell = cell.dom;
								me.eventEl.dom.setAttribute('aria-activedescendant',
										cell.dom.id);
								cell.dom.setAttribute('aria-selected', true);
								cell.addCls(cls);
								me.fireEvent('highlightitem', me, cell);
								break;
							}
						}

						me.eventEl.dom.removeAttribute('aria-busy');
					},

					/**
					 * Update the contents of the picker for a new month
					 * 
					 * @private
					 * @param {Date}
					 *          date The new date
					 */
					fullUpdate : function(date) {
						var me = this, cells = me.cells.elements, textNodes = me.textNodes, disabledCls = me.disabledCellCls, eDate = Ext.Date, i = 0, extraDays = 0, newDate = +eDate
								.clearTime(date, true), today = +eDate.clearTime(new Date()), min = me.minDate ? eDate
								.clearTime(me.minDate, true)
								: Number.NEGATIVE_INFINITY, max = me.maxDate ? eDate.clearTime(
								me.maxDate, true) : Number.POSITIVE_INFINITY, ddMatch = me.disabledDatesRE, ddText = me.disabledDatesText, ddays = me.disabledDays ? me.disabledDays
								.join('')
								: false, ddaysText = me.disabledDaysText, format = me.format, days = eDate
								.getDaysInMonth(date), firstOfMonth = eDate
								.getFirstDateOfMonth(date), startingPos = firstOfMonth.getDay()
								- me.startDay, previousMonth = eDate.add(date, eDate.MONTH, -1), ariaTitleDateFormat = me.ariaTitleDateFormat, prevStart, current, disableToday, tempDate, setCellClass, html, cls, formatValue, value;

						if (startingPos < 0) {
							startingPos += 7;
						}

						days += startingPos;
						prevStart = eDate.getDaysInMonth(previousMonth) - startingPos;
						current = new Date(previousMonth.getFullYear(), previousMonth
								.getMonth(), prevStart, me.initHour);

						if (me.showToday) {
							tempDate = eDate.clearTime(new Date());
							disableToday = (tempDate < min
									|| tempDate > max
									|| (ddMatch && format && ddMatch.test(eDate.dateFormat(
											tempDate, format))) || (ddays && ddays.indexOf(tempDate
									.getDay()) !== -1));

							me.todayDisabled = disableToday;
							if (!me.disabled) {
								me.todayBtn.setDisabled(disableToday);
							}
						}

						setCellClass = function(cellIndex, cls) {
							var cell = cells[cellIndex], describedBy = [];

							// Cells are not rendered with ids
							if (!cell.hasAttribute('id')) {
								cell.setAttribute('id', me.id + '-cell-' + cellIndex);
							}

							// store dateValue number as an expando
							value = +eDate.clearTime(current, true);
							cell.firstChild.dateValue = value;

							cell.setAttribute('aria-label', eDate.format(current,
									ariaTitleDateFormat));

							// Here and below we can't use title attribute instead of
							// data-qtip
							// because JAWS will announce title value before cell content
							// which is not what we need. Also we are using aria-describedby
							// attribute
							// and not placing the text in aria-label because some cells may
							// have
							// compound descriptions (like Today and Disabled day).
							cell.removeAttribute('aria-describedby');
							cell.removeAttribute('data-qtip');

							if (value === today) {
								cls += ' ' + me.todayCls;
								describedBy.push(me.id + '-todayText');
							}

							if (value === newDate) {
								me.activeCell = cell;
								me.eventEl.dom.setAttribute('aria-activedescendant', cell.id);
								cell.setAttribute('aria-selected', true);
								cls += ' ' + me.selectedCls;
								me.fireEvent('highlightitem', me, cell);
							} else {
								cell.setAttribute('aria-selected', false);
							}

							if (value < min) {
								cls += ' ' + disabledCls;
								describedBy.push(me.id + '-ariaMinText');
								cell.setAttribute('data-qtip', me.minText);
							} else if (value > max) {
								cls += ' ' + disabledCls;
								describedBy.push(me.id + '-ariaMaxText');
								cell.setAttribute('data-qtip', me.maxText);
							} else if (ddays && ddays.indexOf(current.getDay()) !== -1) {
								cell.setAttribute('data-qtip', ddaysText);
								describedBy.push(me.id + '-ariaDisabledDaysText');
								cls += ' ' + disabledCls;
							} else if (ddMatch && format) {
								formatValue = eDate.dateFormat(current, format);
								if (ddMatch.test(formatValue)) {
									cell.setAttribute('data-qtip', ddText.replace('%0',
											formatValue));
									describedBy.push(me.id + '-ariaDisabledDatesText');
									cls += ' ' + disabledCls;
								}
							}

							if (describedBy.length) {
								cell.setAttribute('aria-describedby', describedBy.join(' '));
							}

							cell.className = cls + ' ' + me.cellCls;
						};

						me.eventEl.dom.setAttribute('aria-busy', 'true');

						for (; i < me.numDays; ++i) {
							if (i < startingPos) {
								html = (++prevStart);
								cls = me.prevCls;
							} else if (i >= days) {
								html = (++extraDays);
								cls = me.nextCls;
							} else {
								html = i - startingPos + 1;
								cls = me.activeCls;
							}
							textNodes[i].innerHTML = html;
							current.setDate(current.getDate() + 1);
							setCellClass(i, cls);
						}

						me.eventEl.dom.removeAttribute('aria-busy');

						me.monthBtn.setText(Ext.Date.format(date, me.monthYearFormat));

						me.hourField.setValue(date.getHours());
						me.minuteField.setValue(date.getMinutes());
						me.secondField.setValue(date.getSeconds());

					},

					/**
					 * Update the contents of the picker
					 * 
					 * @private
					 * @param {Date}
					 *          date The new date
					 * @param {Boolean}
					 *          forceRefresh True to force a full refresh
					 */
					update : function(date, forceRefresh) {
						var me = this, active = me.activeDate;
						me.hourField.setValue(date.getHours());
						me.minuteField.setValue(date.getMinutes());
						me.secondField.setValue(date.getSeconds());
						if (me.rendered) {
							me.activeDate = date;
							if (!forceRefresh && active && me.el
									&& active.getMonth() === date.getMonth()
									&& active.getFullYear() === date.getFullYear()) {
								me.selectedUpdate(date, active);
							} else {
								me.fullUpdate(date, active);
							}
						}
						return me;
					},

					/**
					 * @private
					 * @inheritdoc
					 */
					beforeDestroy : function() {
						var me = this;
						if (me.rendered) {
							Ext.destroy(me.keyNav, me.monthPicker, me.monthBtn,
									me.nextRepeater, me.prevRepeater, me.todayBtn,
									me.todayElSpan, me.hourField, me.minuteField, me.secondField,
									me.okBtn);
							delete me.textNodes;
							delete me.cells.elements;
						}
						me.callParent();
					},

					privates : {
						// Do the job of a container layout at this point even though we are
						// not a Container.
						// TODO: Refactor as a Container.
						finishRenderChildren : function() {
							var me = this;

							me.callParent();
							me.monthBtn.finishRender();

							me.hourField.finishRender();
							me.minuteField.finishRender();
							me.secondField.finishRender();

							if (me.showToday) {
								me.okBtn.finishRender();
								me.todayBtn.finishRender();
							}
						},

						getFocusEl : function() {
							return this.eventEl;
						},

						/**
						 * Set the disabled state of various internal components
						 * 
						 * @param {Boolean}
						 *          disabled
						 * @private
						 */
						syncDisabled : function(disabled, doButton) {
							var me = this, keyNav = me.keyNav, todayBtn = me.todayBtn;

							// If we have one, we have all
							if (keyNav) {
								keyNav.setDisabled(disabled);
								me.prevRepeater.setDisabled(disabled);
								me.nextRepeater.setDisabled(disabled);
							}
							if (doButton && todayBtn) {
								todayBtn.setDisabled(me.todayDisabled || disabled);
							}
						}
					}
				});

Ext.define("Ext.locale.zh_CN.picker.DateTime", {
	override : "app.ux.picker.DateTime",
	todayText : "现在",
	minText : "日期必须大于最小允许日期",
	// update
	maxText : "日期必须小于最大允许日期",
	// update
	disabledDaysText : "",
	disabledDatesText : "",
	nextText : '下个月 (Ctrl+Right)',
	prevText : '上个月 (Ctrl+Left)',
	monthYearText : '选择一个月 (Control+Up/Down 来改变年份)',
	// update
	todayTip : "{0}",
	format : "Y-m-d H:i:s",
	ariaTitle : '{0}',
	ariaTitleDateFormat : 'Y\u5e74m\u6708d\u65e5',
	longDayFormat : 'Y\u5e74m\u6708d\u65e5',
	monthYearFormat : 'Y\u5e74m\u6708',
	getDayInitial : function(value) {
		// Grab the last character
		return value.substr(value.length - 1);
	}
});
